package geometry;

/**
 * Provides utility methods for geometric objects.
 *
 * @author bsantier
 */
public abstract class Utility
{
	/**
	 * @param point the Point3D to multiply.
	 * @param multiplier the scalar multiplier.
	 * @return a new Point3D that is a scalar multiple of the specified Point3D by the specified amount.
	 */
	public static Point3D multiple(Point3D point, double multiplier)
	{
		return new Point3D(point.getX() * multiplier, point.getY() * multiplier, point.getZ() * multiplier);
	}

	/**
	 * @param point the Point2D to multiply.
	 * @param multiplier the scalar multiplier.
	 * @return a new Point2D that is a scalar multiple of the specified Point3D by the specified amount.
	 */
	public static Point2D multiple(Point2D point, double multiplier)
	{
		return new Point2D(point.getX() * multiplier, point.getY() * multiplier);
	}

	/**
	 * @param angle the Angle to check the cosine of.
	 * @return the cosine of the specified Angle.
	 */
	public static double cos(Angle angle)
	{
		return Math.cos(angle.getAngleRadians());
	}

	/**
	 * @param angle the Angle to check the sine of.
	 * @return the sine of the specified Angle.
	 */
	public static double sin(Angle angle)
	{
		return Math.sin(angle.getAngleRadians());
	}

	/**
	 * @param angle the Angle to check the tangent of.
	 * @return the tangent of the specified Angle.
	 */
	public static double tan(Angle angle)
	{
		return Math.tan(angle.getAngleRadians());
	}

	/**
	 * @param value the cosine of the desired Angle.
	 * @return the smallest Angle whose cosine is the specified value.
	 */
	public static Angle arcCos(double value)
	{
		return new Angle(Math.acos(value));
	}

	/**
	 * @param value the sine of the desired Angle.
	 * @return the smallest Angle whose sine is the specified value.
	 */
	public static Angle arcSin(double value)
	{
		return new Angle(Math.asin(value));
	}

	/**
	 * @param value the tangent of the desired Angle.
	 * @return the smallest Angle whose tangent is the specified value.
	 */
	public static Angle arcTan(double value)
	{
		return new Angle(Math.atan(value));
	}

	/**
	 * Returns the inverse of the specified Angle.  The inverse of an angle is the angle which, when added to that angle, results in an angle of zero.
	 *
	 * @param angle the Angle to invert
	 * @return the inverse of the specified Angle.
	 */
	public static Angle invert(Angle angle)
	{
		return new Angle(-angle.getAngleRadians());
	}

	/**
	 * @param first the first Angle to add.
	 * @param second the Angle to add to the first.
	 * @return the Angle that is the addition of the first and second Angles.
	 */
	public static Angle add(Angle first, Angle second)
	{
		return new Angle(first.getAngleRadians() + second.getAngleRadians());
	}

	/**
	 * @param first the Angle to subtract from.
	 * @param second the Angle to subtract.
	 * @return the Angle that represents what is left from subtracting the second Angle from the first Angle.
	 */
	public static Angle subtract(Angle first, Angle second)
	{
		return new Angle(first.getAngleRadians() - second.getAngleRadians());
	}

	/**
	 * @param a Vector A.
	 * @param b Vector B.
	 * @return the dot product of A and B.
	 */
	public static double dotProduct(Vector3D a, Vector3D b)
	{
		return a.head().getX() * b.head().getX() +  a.head().getY() * b.head().getY() + a.head().getZ() * b.head().getZ();
	}

	/**
	 * @param a Vector A.
	 * @param b Vector B.
	 * @return the dot product of A and B.
	 */
	public static double dotProduct(Vector2D a, Vector2D b)
	{
		return a.head().getX() * b.head().getX() + a.head().getY() * b.head().getY();
	}

	/**
	 * @param a Vector A.
	 * @param b Vector B.
	 * @return the scalar projection of A onto B.
	 */
	public static double scalarProjection(Vector3D a, Vector3D b)
	{
		return a.magnitude() * cos(angleBetween(a, b));
	}

	/**
	 * @param a Vector A.
	 * @param b Vector B.
	 * @return the smallest angle between A and B.
	 */
	public static Angle angleBetween(Vector3D a, Vector3D b)
	{
		return arcCos(dotProduct(a, b) / (a.magnitude() * b.magnitude()));
	}

	/**
	 * @param a Vector A.
	 * @param b Vector B.
	 * @return the smallest angle between A and B.
	 */
	public static Angle angleBetween(Vector2D a, Vector2D b)
	{
		return arcCos(dotProduct(a, b) / (a.magnitude() * b.magnitude()));
	}
}